package org.weasel.views;

import java.net.MalformedURLException;
import java.net.URL;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.DrillDownAdapter;
import org.eclipse.ui.part.ViewPart;
import org.weasel.controllers.TreeController;
import org.weasel.tree.TreeLabelProvider;
import org.weasel.tree.TreeObject;
import org.weasel.tree.TreeObjectIteration;
import org.weasel.tree.TreeObjectProject;
import org.weasel.tree.TreeObjectRelease;
import org.weasel.tree.TreeObjectUnscheduled;
import org.weasel.tree.TreeObjectUserStory;
import org.weasel.tree.TreeParent;

/**
 * This sample class demonstrates how to plug-in a new workbench view. The view
 * shows data obtained from the model. The sample creates a dummy model on the
 * fly, but a real implementation would connect to the model available either in
 * this or another plug-in (e.g. the workspace). The view is connected to the
 * model using a content provider.
 * <p>
 * The view uses a label provider to define how model objects should be
 * presented in the view. Each view can present the same model objects using
 * different labels and icons, if needed. Alternatively, a single label provider
 * can be shared between views in order to ensure that objects of the same type
 * are presented in the same way everywhere.
 * <p>
 */

public class Navigator extends ViewPart {
	public static final String ID = "org.weasel.views.Navigator";

	private TreeViewer viewer;

	private DrillDownAdapter drillDownAdapter;

	private Action refreshAction;

	private Action expandChildsAction;

	private Action doubleClickAction;

	private Action collapseChildsAction;

	private Action userStoryWizardAction;

	private Action deleteAction;
	
	private Action addReleaseAction;

//	private Action createDeveloperAction;

	private TreeController treeController;

	private Action saveAction;

	/*
	 * The content provider class is responsible for providing objects to the
	 * view. It can wrap existing objects in adapters or simply return objects
	 * as-is. These objects may be sensitive to the current input of the view,
	 * or ignore it and always show the same content (like Task List, for
	 * example).
	 */

	private void addDragAndDrop() {
		Transfer[] transferTypes = new Transfer[] { TextTransfer.getInstance() };
		int operations = DND.DROP_MOVE | DND.DROP_COPY;
		final TreeObject[] selectedUserStory = new TreeObject[1];
		final TreeParent[] sourceParent = new TreeParent[1];
		final TreeParent[] targetParent = new TreeParent[1];
		final TreeParent[] targetProject = new TreeParent[1];
		final TreeParent[] sourceProject = new TreeParent[1];
		viewer.addDragSupport(operations, transferTypes,
				new DragSourceListener() {
					public void dragFinished(DragSourceEvent event) {
						// TODO Auto-generated method stub
					}

					public void dragSetData(DragSourceEvent event) {
						// TODO Auto-generated method stub
						event.data = selectedUserStory[0].getName();
					}

					public void dragStart(DragSourceEvent event) {
						// TODO Auto-generated method stub
						event.doit = false;
						ISelection selection = viewer.getSelection();
						Object selectedTreeObject = ((IStructuredSelection) selection)
								.getFirstElement();
						if (selectedTreeObject instanceof TreeObjectUserStory) {
							selectedUserStory[0] = (TreeObject) selectedTreeObject;
							sourceProject[0] = getProjectObjectOfTreeObject(selectedUserStory[0]);
							sourceParent[0] = selectedUserStory[0].getParent();
							event.doit = true;
						}
					}

					private TreeObjectProject getProjectObjectOfTreeObject(
							TreeObject to) {
						TreeObject iterator = to;
						while (!(iterator instanceof TreeObjectProject)) {
							iterator = iterator.getParent();
						}
						return (TreeObjectProject) iterator;
					}
				});

		viewer.addDropSupport(operations, transferTypes,
				new DropTargetListener() {
					public void dragEnter(DropTargetEvent event) {
						// TODO Auto-generated method stub
					}

					public void dragLeave(DropTargetEvent event) {
						// TODO Auto-generated method stub

					}

					public void dragOperationChanged(DropTargetEvent event) {
						// TODO Auto-generated method stub

					}

					public void dragOver(DropTargetEvent event) {
						// TODO Auto-generated method stub
						event.detail = DND.DROP_NONE;
						TreeItem item = (TreeItem) event.item;
						if (item != null) {
							if (item.getData() instanceof TreeParent) {
								TreeParent possibleDestination = (TreeParent) item
										.getData();
								if (possibleDestination instanceof TreeObjectIteration
										|| possibleDestination instanceof TreeObjectUnscheduled) {
									targetProject[0] = getProjectObjectOfTreeObject(possibleDestination);
									if (targetProject[0].getName().equals(
											sourceProject[0].getName())) {
										targetParent[0] = possibleDestination;
										event.detail = DND.DROP_MOVE;
									}
								}
							}
						}
					}

					private TreeObjectProject getProjectObjectOfTreeObject(
							TreeObject to) {
						TreeObject iterator = to;
						while (!(iterator instanceof TreeObjectProject)) {
							iterator = iterator.getParent();
						}
						return (TreeObjectProject) iterator;
					}

					public void drop(DropTargetEvent event) {
						// TODO Auto-generated method stub
						if (event.data == null) {
							event.detail = DND.DROP_NONE;
							return;
						}
						TreeController.instance().moveUserStory(
								selectedUserStory[0], targetParent[0],
								sourceParent[0]);
					}

					public void dropAccept(DropTargetEvent event) {
						// TODO Auto-generated method stub
					}
				});
	}

	class ViewContentProvider implements IStructuredContentProvider,
			ITreeContentProvider {
		private TreeParent invisibleRoot;

		public void inputChanged(Viewer v, Object oldInput, Object newInput) {
			initialize();
		}

		public void dispose() {
		}

		public Object[] getElements(Object parent) {
			if (parent.equals(getViewSite())) {
				if (invisibleRoot == null) {
					initialize();
				}
				return getChildren(invisibleRoot);
			}
			return getChildren(parent);
		}

		public Object getParent(Object child) {
			if (child instanceof TreeObject) {
				return ((TreeObject) child).getParent();
			}
			return null;
		}

		public Object[] getChildren(Object parent) {
			if (parent instanceof TreeParent) {
				return ((TreeParent) parent).getChildren();
			}
			return new Object[0];
		}

		public boolean hasChildren(Object parent) {
			if (parent instanceof TreeParent) {
				return ((TreeParent) parent).hasChildren();
			}
			return false;
		}

		/*
		 * This method is called whenever the input of the tree changes. The
		 * initial loading of the tree is done in the createPartControl method.
		 */
		private void initialize() {

		}

	}

	class ViewLabelProvider extends LabelProvider {

		@Override
		public String getText(Object obj) {
			return obj.toString();
		}

		@Override
		public Image getImage(Object obj) {
			String imageKey = ISharedImages.IMG_OBJ_FILE;
			if (obj instanceof TreeParent) {
				imageKey = ISharedImages.IMG_OBJ_FOLDER;
			}
			return PlatformUI.getWorkbench().getSharedImages().getImage(
					imageKey);
		}
	}

	class NameSorter extends ViewerSorter {
	}

	/**
	 * The constructor.
	 */
	public Navigator() {

	}

	public TreeViewer getTreeViewer() {
		return viewer;
	}

	public void setTreeController(TreeController treeController) {
		this.treeController = treeController;
	}

	/**
	 * This is a callback that will allow us to create the viewer and initialize
	 * it. This method creates and initializes the Navigator view.
	 */
	@Override
	public void createPartControl(Composite parent) {
		viewer = new TreeViewer(parent, SWT.SINGLE | SWT.H_SCROLL
				| SWT.V_SCROLL);
		addDragAndDrop();
		drillDownAdapter = new DrillDownAdapter(viewer);
		viewer.setContentProvider(new ViewContentProvider());
		viewer.setLabelProvider(new TreeLabelProvider());
//		viewer.setSorter(new NameSorter());
		// Here the tree is created and initialized after the loading of the
		// Navigator
		makeActions();
		saveAction.setEnabled(false);
		hookContextMenu();
		hookDoubleClickAction();
		contributeToActionBars();

		TreeController treeController = TreeController.instance();
		treeController.setTreeViewer(viewer);
		treeController.setNavigator(this);
		treeController.displayTree();
	}

	private void hookContextMenu() {
		MenuManager menuMgr = new MenuManager("#PopupMenu");
		menuMgr.setRemoveAllWhenShown(true);
		menuMgr.addMenuListener(new IMenuListener() {
			public void menuAboutToShow(IMenuManager manager) {
				Navigator.this.fillContextMenu(manager);
				enableDisableButtons();
			}
		});
		Menu menu = menuMgr.createContextMenu(viewer.getControl());
		viewer.getControl().setMenu(menu);
		getSite().registerContextMenu(menuMgr, viewer);
	}

	/**
	 * This method enables or disables some functionalities based on the
	 * selected item of the tree.
	 * 
	 */
	private void enableDisableButtons() {
		TreeObject selected = getSelectedTreeObject();
		if (selected instanceof TreeObjectProject
				|| selected instanceof TreeObjectRelease
				|| selected instanceof TreeObjectIteration
				|| selected instanceof TreeObjectUnscheduled) {
			userStoryWizardAction.setEnabled(true);
		} else {
			userStoryWizardAction.setEnabled(false);
		}
		if (selected instanceof TreeObjectProject
				|| selected instanceof TreeObjectRelease
				|| selected instanceof TreeObjectIteration
				|| selected instanceof TreeObjectUserStory) {
			deleteAction.setEnabled(true);
		} else {
			deleteAction.setEnabled(false);
		}
		
		if(selected instanceof TreeObjectProject
				|| selected.getName().equals("Releases"))
			addReleaseAction.setEnabled(true);
		else
			addReleaseAction.setEnabled(false);
	}

	private void contributeToActionBars() {
		IActionBars bars = getViewSite().getActionBars();
		fillLocalPullDown(bars.getMenuManager());
		fillLocalToolBar(bars.getToolBarManager());
	}

	private void fillLocalPullDown(IMenuManager manager) {
		manager.add(refreshAction);
		manager.add(new Separator());
		manager.add(expandChildsAction);
		manager.add(collapseChildsAction);
	}

	private void fillContextMenu(IMenuManager manager) {
		manager.add(refreshAction);
		manager.add(expandChildsAction);
		manager.add(collapseChildsAction);
		manager.add(userStoryWizardAction);
		manager.add(deleteAction);
		manager.add(addReleaseAction);
		manager.add(new Separator());
		drillDownAdapter.addNavigationActions(manager);
		// Other plug-ins can contribute there actions here
		manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
	}

	private void fillLocalToolBar(IToolBarManager manager) {
		manager.add(saveAction);
		manager.add(refreshAction);
		manager.add(expandChildsAction);
		manager.add(collapseChildsAction);
		manager.add(new Separator());
		drillDownAdapter.addNavigationActions(manager);
	}

	private void makeActions() {
		saveAction = new Action() {
			@Override
			public void run() {
				TreeController.instance().saveChanges();
			}
		};
		saveAction.setText("save");
		saveAction.setToolTipText("Save the changes");
		try {
			saveAction.setImageDescriptor(ImageDescriptor
					.createFromURL(new URL("file:icons/save.gif")));
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		refreshAction = new Action() {
			@Override
			public void run() {
				viewer.refresh();
			}
		};
		refreshAction.setText("refresh");
		refreshAction.setToolTipText("Refresh the project tree");
		try {
			refreshAction.setImageDescriptor(ImageDescriptor
					.createFromURL(new URL("file:icons/refresh.gif")));
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		expandChildsAction = new Action() {
			@Override
			public void run() {
				TreeController.instance().expandChilds(getSelectedTreeObject());
			}
		};
		expandChildsAction.setText("Expand all");
		expandChildsAction
				.setToolTipText("Expands all the children of a selected node and the node itself.");
		try {
			expandChildsAction.setImageDescriptor(ImageDescriptor
					.createFromURL(new URL("file:icons/expandall.gif")));
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		collapseChildsAction = new Action() {
			@Override
			public void run() {
				TreeController.instance().collapseChilds(
						getSelectedTreeObject());
			}
		};
		collapseChildsAction.setText("Collapse all");
		collapseChildsAction
				.setToolTipText("Collapses all the children of a selected node and the node itself.");
		try {
			collapseChildsAction.setImageDescriptor(ImageDescriptor
					.createFromURL(new URL("file:icons/collapseall.gif")));
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		doubleClickAction = new Action() {
			@Override
			public void run() {
				treeController = TreeController.instance();
				treeController.doDoubleClickAction();
			}
		};

		userStoryWizardAction = new Action() {
			@Override
			public void run() {
				// the add user story button will be disabled if not a valid
				// treeParent is selected
				TreeParent selectedTreeObject = (TreeParent) getSelectedTreeObject();
				treeController = TreeController.instance();
				treeController.showUserStoryWizard(selectedTreeObject);
			}
		};

		userStoryWizardAction.setText("Add userstory");
		try {
			userStoryWizardAction.setImageDescriptor(ImageDescriptor
					.createFromURL(new URL("file:icons/add_userstory.gif")));
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		deleteAction = new Action() {
			@Override
			public void run() {
				TreeController.instance().removeSelectedObject();
			}
		};
		deleteAction.setText("Delete");
		try {
			deleteAction.setImageDescriptor(ImageDescriptor
					.createFromURL(new URL("file:icons/delete.gif")));
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		addReleaseAction = new Action() {
			@Override
			public void run() {
				TreeController.instance().addRelease();
			}
		};
		addReleaseAction.setText("Add release");
		try {
			addReleaseAction.setImageDescriptor(ImageDescriptor
					.createFromURL(new URL("file:icons/add.gif")));
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

//		createDeveloperAction = new Action() {
//			@Override
//			public void run() {
//				treeController = TreeController.instance();
//				treeController.showDeveloperWizard();
//			}
//		};
//		createDeveloperAction.setText("Create a new Developer");
//		createDeveloperAction
//				.setToolTipText("Click to create a new developer.");
//		try {
//			createDeveloperAction.setImageDescriptor(ImageDescriptor
//					.createFromURL(new URL("file:icons/add_developer.gif")));
//		} catch (MalformedURLException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
	}

	private TreeObject getSelectedTreeObject() {
		try {
			ISelection selection = viewer.getSelection();
			TreeObject selectedTreeObject = (TreeObject) ((IStructuredSelection) selection)
					.getFirstElement();
			return selectedTreeObject;
		} catch (Exception e) {
			System.out
					.println("Exception caught: Navigator.getSelectedTreeObject()");
			return null;
		}
	}

	private void hookDoubleClickAction() {
		viewer.addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				doubleClickAction.run();
			}
		});
	}

	/**
	 * Passing the focus request to the viewer's control.
	 */
	@Override
	public void setFocus() {
		viewer.getControl().setFocus();
	}

	public Action getSaveAction() {
		return saveAction;
	}

}